﻿using Canal.UserServiceSync.Context;
using Canal.UserServiceSync.Models;
using CanalSharp.Connections;
using CanalSharp.Protocol;
using Microsoft.Extensions.Caching.Distributed;
using Newtonsoft.Json;
using System.Reflection;
using Type = System.Type;
namespace Canal.UserServiceSync.CanalSyncService
{
    public class UserInfoSync
    {

        private readonly IDistributedCache _distributedCache;
        public UserInfoSync(IDistributedCache distributedCache)
        {
            _distributedCache = distributedCache;
        }
        /// <summary>
        /// 开始同步
        /// </summary>
        public async void Start()
        {
            //1、创建Canal日志
            var loggerFactory = LoggerFactory.Create(builder =>
            {
                builder
                .AddFilter("Microsoft", LogLevel.Debug)
                .AddFilter("System", LogLevel.Information)
                .AddConsole();
            });
            var logger = loggerFactory.CreateLogger<SimpleCanalConnection>();
            //2、建立canal服务器连接
            /* 127.0.0.1 canal服务器地址
             * 11111 canal端口
             * 101 在canal服务器配置文件instance.properties中的slaveId
             * **/
            var canalConn = new SimpleCanalConnection(new SimpleCanalOptions("127.0.0.1", 11111, "101"), logger);
            await canalConn.ConnectAsync();
            //3、订阅Canal数据
            await canalConn.SubscribeAsync("ndc_userservice.userinfo");
            //4、读取canal数据
            while (true)
            {
                try
                {
                    //4.1、指定读取大小
                    var message = await canalConn.GetWithoutAckAsync(1024);
                    var message1 = await canalConn.GetAsync(1024);
                    //4.2、判断数据类型
                    var entries = message.Entries;
                    foreach (var entry in entries)
                    {
                        //如果目标数据在事务执行中，则不处理（数据加锁的不处理）
                        if (entry.EntryType == EntryType.Transactionbegin || entry.EntryType == EntryType.Transactionend)
                        {
                            continue;
                        }
                        //5、根据数据操作类型分开处理（增删改查）
                        RowChange rowChange = RowChange.Parser.ParseFrom(entry.StoreValue);
                        //5.1、当数据表patient_info增加数据时触发
                        if (rowChange.EventType == EventType.Insert)
                        {
                            Console.WriteLine("增加数据...................");
                            //5.1.1、获取具体增加的数据
                            foreach (var rowData in rowChange.RowDatas)
                            {
                                //5.1.2、获取具体的数据列
                                List<Column> columns = rowData.AfterColumns.ToList();
                                //5.1.3、处理获取到的数据，写入目标数据库
                                InsertPatientData(columns);
                            }
                        }
                        //5.2、删除数据
                        if (rowChange.EventType == EventType.Delete)
                        {
                            Console.WriteLine("删除数据");
                        }
                        //5.3、修改数据
                        if (rowChange.EventType == EventType.Update)
                        {
                            Console.WriteLine("修改数据");
                        }
                        //5.4、查询数据
                        if (rowChange.EventType == EventType.Query)
                        {
                            Console.WriteLine("查询数据");
                        }

                    }
                }
                catch (Exception e)
                {
                    logger.LogError(e, "Error;");
                    //当canal集群部署发生异常执行重连，此时canal需要负载均衡
                }
            }
        }
        /// <summary>
        /// 获取用户表数据
        /// </summary>
        /// <param name="columns"></param>
        private void InsertPatientData(List<Column> columns)
        {
            try
            {
                //通过反射实现数据转换
                //1、创建userInfo
                UserInfo userInfo = new UserInfo();
                //2、反射userInfo
                Type type = typeof(UserInfo);
                //3、从columns中取出具体的数据
                foreach (Column column in columns)
                {
                    //3.1、取出字段名
                    var fieldName = column.Name;
                    //3.2、取出字段值
                    var fieldValue = column.Value;
                    //3.3、通过属性对userInfo对象赋值
                    PropertyInfo propertyInfo = type.GetProperty(fieldName);
                    //3.4、数据类型转换
                    if (propertyInfo != null)
                    {
                        Type columnType = propertyInfo.PropertyType;

                        if (columnType == typeof(int))
                        {
                            propertyInfo.SetValue(userInfo, int.Parse(fieldValue));
                        }
                        else if (columnType == typeof(long))
                        {
                            propertyInfo.SetValue(userInfo, long.Parse(fieldValue));
                        }
                        else if (columnType == typeof(bool))
                        {
                            propertyInfo.SetValue(userInfo, bool.Parse(fieldValue));
                        }
                        else if (columnType == typeof(DateTime))
                        {
                            propertyInfo.SetValue(userInfo, DateTime.Parse(fieldValue));
                        }
                        else if (columnType == typeof(string))
                        {
                            propertyInfo.SetValue(userInfo, fieldValue);
                        }
                        else
                        {
                            Console.WriteLine($"未知的数据类型{columnType}");
                        }
                    }
                }
                //3.5、保存数据
                var dataRow = JsonConvert.SerializeObject(userInfo);
                Console.WriteLine($"dataRow : {dataRow}");
                _distributedCache.SetString($"userInfo_{userInfo.UserName}", dataRow);
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
}